﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Globalization;

namespace BehaviorFramework
{
    public static class TypeConversionExtensions
    {
        // converts the source object variable value to the desired result type     
        public static TR ChangeType<TR>(this object value)
        {
            return (TR)ChangeType(value, typeof(TR));
        }

        // converts the source value, and if null returns the specified default value
        public static TR ChangeType<TR>(this object value, TR whenNull)
        {
            return (value == null || value is DBNull) ? whenNull : (TR)ChangeType(value, typeof(TR));
        }

        public static object ChangeType(this object value, Type convertToType)
        {
            if (convertToType == null) { throw new ArgumentNullException("convertToType"); }

            // "null" string handling
            if (value != null && value.GetType() == typeof(string) && (((string)value).ToLower() == "null" || ((string)value).ToLower() == "undefined"))
            {
                value = null;
            }

            // return null if the value is null or DBNull        
            if (value == null || value is DBNull || string.IsNullOrWhiteSpace(value.ToString()))
            {
                if (convertToType.IsGenericType && convertToType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                {
                    return null;
                    //return convertToType.UnderlyingSystemType.dconvertToType = Nullable.GetUnderlyingType(convertToType);
                }
                object obj = convertToType.IsValueType ?
                        Activator.CreateInstance(convertToType) :
                        obj = null;

                return obj;
            }

            // non-nullable types, which are not supported by Convert.ChangeType(),
            // unwrap the types to determine the underlying time
            if (convertToType.IsGenericType && convertToType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                convertToType = Nullable.GetUnderlyingType(convertToType);
            }

            // deal with conversion to enum types when input is a string
            if (convertToType.IsEnum && value is string)
            {
                return Enum.Parse(convertToType, value as string);
            }
            // deal with conversion to enum types when input is a integral primitive
            if (value != null && convertToType.IsEnum && value.GetType().IsPrimitive &&
                !(value is bool) && !(value is char) &&
                !(value is float) && !(value is double))
            {
                return Enum.ToObject(convertToType, value);
            }

            if (convertToType.IsIntegralType())
            {
                var decimalSep = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
                var groupSep = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
                var negIndicator = CultureInfo.CurrentCulture.NumberFormat.NegativeSign;
                var strValue = value.ToString();

                strValue = strValue.Replace(groupSep, "");

                if (!convertToType.IsDecimalType())
                {
                    strValue = strValue.Split(decimalSep.ToCharArray())[0];
                }

                if (string.IsNullOrEmpty(strValue) || strValue == negIndicator)
                {
                    strValue = strValue + "0";
                }
                value = strValue;
            }

            if (convertToType == typeof(bool))
            {
                if (value.ToString().ToLower() == "true" || value.ToString() == "1" || value.ToString().ToLower() == "y")
                {
                    return true;
                }
                if (value.ToString().ToLower() == "false" || value.ToString() == "0" || value.ToString().ToLower() == "n")
                {
                    return false;
                }
            }

            // use Convert.ChangeType() to do all other conversions        

            return Convert.ChangeType(value, convertToType, CultureInfo.InvariantCulture);
        }

        public static bool IsIntegralType(this Type type)
        {
            var integralTypes = new List<Type>()
			{
				typeof(byte),
				typeof(int),
				typeof(uint),
				typeof(short),
				typeof(ushort),
				typeof(long),
				typeof(ulong),
				typeof(sbyte),
				typeof(decimal),
				typeof(double),
				typeof(float),
			};

            return integralTypes.Any(t => t == type);
        }

        public static bool IsDecimalType(this Type type)
        {
            var decimalTypes = new List<Type>()
			{
				typeof(decimal),
				typeof(double),
				typeof(float),
			};

            return decimalTypes.Any(t => t == type);
        }

    }
}
